import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui' as ui;

import 'package:example/widget/template/template_entity.dart';
import 'package:flutter/material.dart';
import 'package:flutter_scaffold/flutter_scaffold.dart';
import 'package:qr_flutter/qr_flutter.dart';

/// 模版编辑
/// @data:2025/7/29
/// @author:zhibo.huang
class TemplateEditView extends StatefulWidget {
  final Template template;
  final double width;
  double _height;
  double _scale;
  final Color selectLineColor;
  final OnCheckedIndexChanged? onCheckedIndexChanged;

  TemplateEditView({
    super.key,
    required this.template,
    required this.width,
    this.onCheckedIndexChanged,
    this.selectLineColor = Colors.red,
  }) : _height = width / template.templateWidth * template.templateHeight,
       _scale = width / template.templateWidth;

  @override
  State<TemplateEditView> createState() => TemplateEditViewState();
}

class TemplateEditViewState extends State<TemplateEditView> {
  int checkedLableId = -1;
  DragAction dragAction = DragAction.none;
  Offset? lastTouch;
  final double dragRangeFactor = 0.2;
  final MINWIDTH = 120.w;

  late Template template;

  final Map<String, ui.Image> _imageBitmaps = {}; // 图片缓存

  @override
  void initState() {
    super.initState();
    template = widget.template;
    template.addListener(() {
      checkedLableId = template.checkLabelId;
      _generateImageBitmaps(template, _imageBitmaps);
      setState(() {});
    });
    Future.wait([_updateTemplateBackground(template, _imageBitmaps), _generateImageBitmaps(template, _imageBitmaps)]);
  }

  Future<void> _generateQrBitmaps(TemplateLabel label, Map<String, ui.Image> imageBitmaps) async {
    final painter = QrPainter(data: label.text ?? "", version: QrVersions.auto, gapless: true);
    final pic = await painter.toImage(label.width);
    imageBitmaps[label.text ?? ""] = pic;
    setState(() {});
  }

  Future<void> _generateImageBitmaps(Template template, Map<String, ui.Image> imageBitmaps) async {
    for (var label in template.labels) {
      try {
        if (label.labelType == LabelType.image && imageBitmaps[label.imageUrl!] == null) {
          final ui.Image image = await _loadUiImage(label.imageUrl!, label.width, label.height);
          imageBitmaps[label.imageUrl!] = image;
          setState(() {});
        } else if (label.labelType == LabelType.qrcode && imageBitmaps[label.text] == null) {
          _generateQrBitmaps(label, imageBitmaps);
        }
      } catch (e) {
        debugPrint('加载图片失败: ${label.imageUrl}, error: $e');
      }
    }
  }

  @override
  void deactivate() {
    super.deactivate();
    template.dispose();
  }

  Future<void> _updateTemplateBackground(Template template, Map<String, ui.Image> imageBitmaps) async {
    if (template.backgroundImage == null || imageBitmaps[template.backgroundImage] != null) {
      return;
    }
    final ui.Image image = await _loadUiImage(
      template.backgroundImage!,
      template.templateWidth,
      template.templateWidth,
    );
    imageBitmaps[template.backgroundImage!] = image;
    setState(() {});
  }

  Future<ui.Image> _loadUiImage(String imageUrl, double width, double height) async {
    final Completer<ui.Image> completer = Completer();

    ImageProvider provider;
    if (imageUrl.startsWith('http') || imageUrl.startsWith('https')) {
      provider = NetworkImage(imageUrl);
    } else if (imageUrl.startsWith('assets/')) {
      provider = AssetImage(imageUrl);
    } else {
      provider = FileImage(File(imageUrl));
    }
    final ImageStream stream = provider.resolve(ImageConfiguration(size: Size(width, height)));
    late final ImageStreamListener listener;
    listener = ImageStreamListener(
      (ImageInfo info, bool _) {
        completer.complete(info.image);
        stream.removeListener(listener);
      },
      onError: (dynamic error, StackTrace? stackTrace) {
        completer.completeError(error, stackTrace);
        stream.removeListener(listener);
      },
    );
    stream.addListener(listener);

    return completer.future;
  }

  Rect _getElementRect({
    required double x,
    required double y,
    required double width,
    required double height,
    double offsetX = 0,
    double offsetY = 0,
    double offsetSize = 0,
    bool fixHeight = true,
  }) {
    return Rect.fromLTWH(x + offsetX, y + offsetY, width + offsetSize, fixHeight ? height : height + offsetSize);
  }

  bool _isNearRightEdge(Offset point, Rect rect) {
    return point.dx >= rect.right - rect.width * dragRangeFactor &&
        point.dx <= rect.right + rect.width * dragRangeFactor &&
        point.dy >= rect.top &&
        point.dy <= rect.bottom + rect.bottom * dragRangeFactor;
  }

  void _setCheckedIndex(int index) {
    if (template.checkLabelId != index) {
      setState(() {
        checkedLableId = template.checkLabelId = index;
      });
      widget.onCheckedIndexChanged?.call(index);
    }
  }

  double _clamp(double val, double min, double max) {
    if (val < min) return min;
    if (val > max) return max;
    return val;
  }

  void _updateOffsetsOnMove({required TemplateLabel label, required double dx, required double dy}) {
    double newOffsetX = label.offsetX + dx;
    double newOffsetY = label.offsetY + dy;
    newOffsetX = _clamp(newOffsetX, -label.x, widget.width / widget._scale - label.x - label.width - label.offsetSize);
    newOffsetY = _clamp(newOffsetY, -label.y, widget._height / widget._scale - label.y - label.height);

    label.offsetX = newOffsetX;
    label.offsetY = newOffsetY;
  }

  void _updateOffsetSize({required TemplateLabel label, required double dx}) {
    double newOffsetSize = label.offsetSize + dx;

    newOffsetSize = newOffsetSize < MINWIDTH - label.width ? MINWIDTH - label.width : newOffsetSize;

    double maxWidth = widget.width / widget._scale - label.x - label.offsetX;
    if (label.width + newOffsetSize > maxWidth) {
      newOffsetSize = maxWidth - label.width;
    }

    label.offsetSize = newOffsetSize;
  }

  Future<Uint8List> captureToImage({
    required double width,
    required double height,
    double scale = 1.0,
    Template? templateData,
  }) async {
    final recorder = ui.PictureRecorder();
    final canvas = Canvas(recorder);
    Map<String, ui.Image> imageBitmaps = {};
    if (templateData != null) {
      imageBitmaps.clear();
      await Future.wait([
        _updateTemplateBackground(templateData, imageBitmaps),
        _generateImageBitmaps(templateData, imageBitmaps),
      ]);
    } else {
      templateData ??= template;
      imageBitmaps = _imageBitmaps;
    }

    final painter = TemplatePainter(
      template: templateData,
      width: width,
      height: height,
      scale: scale,
      imageBitmaps: imageBitmaps,
      clearSelectStatus: true,
    );

    painter.paint(canvas, Size(width, height));

    final picture = recorder.endRecording();
    final image = await picture.toImage(width.toInt(), height.toInt());

    final byteData = await image.toByteData(format: ui.ImageByteFormat.rawRgba);
    return byteData!.buffer.asUint8List();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      behavior: HitTestBehavior.translucent,
      onPanStart: (details) {
        final pos = details.localPosition / widget._scale;
        lastTouch = pos;
        if (checkedLableId < 0) {
          dragAction = DragAction.none;
          return;
        }
        final label = _findLabel(checkedLableId);
        if (label == null) {
          return;
        }
        final rect = _getElementRect(
          x: label.x,
          y: label.y,
          width: label.width,
          height: label.height,
          offsetX: label.offsetX,
          offsetY: label.offsetY,
          offsetSize: label.offsetSize,
          fixHeight: label.labelType == LabelType.text,
        );

        dragAction =
            _isNearRightEdge(pos, rect)
                ? DragAction.resize
                : rect.contains(pos)
                ? DragAction.move
                : DragAction.none;
      },
      onPanUpdate: (details) {
        if (dragAction == DragAction.none) return;
        final pos = details.localPosition / widget._scale;
        final dx = pos.dx - lastTouch!.dx;
        final dy = pos.dy - lastTouch!.dy;

        setState(() {
          if (checkedLableId >= 0) {
            final label = _findLabel(checkedLableId);
            if (label == null) {
              return;
            }
            if (dragAction == DragAction.move) {
              _updateOffsetsOnMove(label: label, dx: dx, dy: dy);
            } else if (dragAction == DragAction.resize && label.labelType == LabelType.text) {
              _updateOffsetSize(label: label, dx: dx);
            } else if (dragAction == DragAction.resize &&
                (label.labelType == LabelType.qrcode || label.labelType == LabelType.image)) {
              double delta = dx.abs() > dy.abs() ? dx : dy;
              double newSize = label.width + delta;
              // 限制最大宽度，确保元素不会超出父容器
              double maxSize = widget.width / widget._scale - label.x - label.offsetX;
              double maxHeight = widget._height / widget._scale - label.y - label.offsetY;
              double maxAllowedSize = maxSize < maxHeight ? maxSize : maxHeight;

              if (newSize < MINWIDTH) newSize = MINWIDTH;
              if (newSize > maxAllowedSize) newSize = maxAllowedSize;

              label.width = newSize;
              label.height = newSize; // 保持宽高等比
            }
          }
        });

        lastTouch = pos;
      },
      onPanEnd: (details) {
        setState(() {
          if (checkedLableId >= 0) {
            final label = _findLabel(checkedLableId);
            if (label == null) {
              return;
            }
            label.x += label.offsetX;
            label.y += label.offsetY;
            label.width += label.offsetSize;
            label.offsetX = 0;
            label.offsetY = 0;
            label.offsetSize = 0;
          }
          dragAction = DragAction.none;
        });
      },
      onTapUp: (details) {
        final pos = details.localPosition / widget._scale;
        bool found = false;

        for (int i = template.labels.length - 1; i >= 0; i--) {
          final label = template.labels[i];
          final rect = _getElementRect(
            x: label.x,
            y: label.y,
            width: label.width,
            height: label.height,
            fixHeight: label.labelType == LabelType.text,
          );
          if (rect.contains(pos)) {
            _setCheckedIndex(label.id);
            found = true;
            break;
          }
        }

        if (!found) _setCheckedIndex(-1);
      },
      child: CustomPaint(
        size: Size(widget.width, widget._height),
        painter: TemplatePainter(
          template: template,
          width: widget.width,
          height: widget._height,
          scale: widget._scale,
          imageBitmaps: _imageBitmaps,
          selectLineColor: widget.selectLineColor,
        ),
      ),
    );
  }

  TemplateLabel? _findLabel(int id) {
    try {
      return template.labels.firstWhere((item) => item.id == checkedLableId);
    } catch (_) {
      return null;
    }
  }
}

class TemplatePainter extends CustomPainter {
  final Template template;
  final double width;
  final double height;
  final double scale;
  final Color selectLineColor;
  final Map<String, ui.Image> imageBitmaps;
  bool clearSelectStatus = false;
  final mPaint = Paint();

  TemplatePainter({
    required this.template,
    required this.width,
    required this.height,
    required this.scale,
    required this.imageBitmaps,
    this.selectLineColor = Colors.red,
    this.clearSelectStatus = false,
  });

  @override
  void paint(Canvas canvas, Size size) {
    canvas.save(); // 保存当前 canvas 状态
    canvas.clipRect(Rect.fromLTWH(0, 0, width, height)); // 限定裁剪区域
    var background = imageBitmaps[template.backgroundImage];
    if (background != null) {
      final src = Rect.fromLTWH(0, 0, background.width.toDouble(), background.height.toDouble());
      final dst = Rect.fromLTWH(0, 0, width, height);
      canvas.drawImageRect(background, src, dst, mPaint);
    } else if (template.backgroundColor != null) {
      canvas.drawColor(Color(template.backgroundColor!.toInt()), BlendMode.src);
    }

    for (int i = 0; i < template.labels.length; i++) {
      final label = template.labels[i];

      if (label.labelType == LabelType.text) {
        _drawText(canvas, label, label.id == template.checkLabelId);
      } else if (label.labelType == LabelType.qrcode) {
        _drawQrImage(canvas, label, label.id == template.checkLabelId);
      } else if (label.labelType == LabelType.image) {
        _drawImage(canvas, label, label.id == template.checkLabelId);
      }
    }
    canvas.restore(); // 恢复之前状态
  }

  @override
  bool shouldRepaint(covariant TemplatePainter oldDelegate) => true;

  void _drawBorder(Canvas canvas, Rect rect) {
    if (clearSelectStatus) {
      return;
    }
    final borderPaint =
        Paint()
          ..color = selectLineColor
          ..style = PaintingStyle.stroke
          ..strokeWidth = 1.w;
    canvas.drawRect(rect, borderPaint);
  }

  void _drawText(Canvas canvas, TemplateLabel label, bool isBoreder) {
    final textPainter = TextPainter(
      text: TextSpan(
        text: label.text ?? "",
        style: TextStyle(
          fontSize: label.textSize! * scale,
          color: Color(label.textColor?.toInt() ?? 0x00000000),
          fontFamily: label.fontFamily,
        ),
      ),
      textAlign: TextAlign.values[label.textAlign?.index ?? LabelTextAlign.left.index],
      textDirection: TextDirection.ltr,
    );

    final dx = (label.x + label.offsetX) * scale;
    final dy = (label.y + label.offsetY) * scale;
    final maxWidth = (label.width + label.offsetSize) * scale;
    var textStartX = dx;
    textPainter.layout(minWidth: 0, maxWidth: maxWidth);
    if (label.textAlign == LabelTextAlign.center) {
      textStartX += (maxWidth - textPainter.width) / 2;
    }
    if (label.textAlign == LabelTextAlign.right) {
      textStartX += (maxWidth - textPainter.width);
    }
    textPainter.paint(canvas, Offset(textStartX, dy));
    label.height = textPainter.height / scale;

    if (isBoreder) {
      _drawBorder(canvas, Rect.fromLTWH(dx, dy, maxWidth, textPainter.height));
    }
  }

  void _drawQrImage(Canvas canvas, TemplateLabel label, bool isBoreder) {
    final qrBitmap = imageBitmaps[label.text];
    if (qrBitmap != null) {
      final qrX = (label.x + label.offsetX) * scale;
      final qrY = (label.y + label.offsetY) * scale;
      final qrWidth = (label.width + label.offsetSize) * scale;
      final qrHeight = (label.height + label.offsetSize) * scale;
      final src = Rect.fromLTWH(0, 0, qrBitmap.width.toDouble(), qrBitmap.height.toDouble());
      final dst = Rect.fromLTWH(qrX, qrY, qrWidth, qrHeight);
      canvas.drawImageRect(qrBitmap, src, dst, mPaint);

      if (isBoreder) {
        _drawBorder(canvas, dst);
      }
    }
  }

  void _drawImage(Canvas canvas, TemplateLabel label, bool isBoreder) {
    final imgBitmap = imageBitmaps[label.imageUrl];
    final imgX = (label.x + label.offsetX) * scale;
    final imgY = (label.y + label.offsetY) * scale;
    final imgWidth = (label.width + label.offsetSize) * scale;
    final imgHeight = (label.height + label.offsetSize) * scale;
    final dst = Rect.fromLTWH(imgX, imgY, imgWidth, imgHeight);
    if (imgBitmap != null) {
      final src = Rect.fromLTWH(0, 0, imgBitmap.width.toDouble(), imgBitmap.height.toDouble());
      canvas.drawImageRect(imgBitmap, src, dst, mPaint);
    } else {
      // 图片未加载完成，画灰色占位
      final placeholderPaint = Paint()..color = Colors.grey.withValues(alpha: 0.4);
      canvas.drawRect(dst, placeholderPaint);
    }
    if (isBoreder) {
      _drawBorder(canvas, dst);
    }
  }
}

typedef OnCheckedIndexChanged = void Function(int checkedIndex);
